﻿

namespace MagicWatering.Scheduler.Engine
{
    using System;

    using MagicWatering.Data.DataModel;
    using MagicWatering.Interfaces;
    using MagicWatering.Scheduler.Controller;
    using MagicWatering.Scheduler.Interfaces;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Xml;

    /// <summary>
    /// Manager of the watering m_plan, which starts, stops and controls the execution of the m_plan.
    /// The manager watches the changes in the saved watering m_plan and updates the execution 
    /// if the m_plan was modified outside of the application.
    /// </summary>
    public class WateringManager
    {
        #region Constants
        private const string s_SettingsFileName = "magicwatering.xml";
        #endregion

        private readonly string s_FullSettingName;
        private readonly FileSystemWatcher s_PlanFileWatcher;
        private readonly WateringProgramScheduler s_Scheduler;

        public WateringPlan Plan
        {
            get;
            private set;
        }

        /// <summary>
        /// Creates new file manager to load, save and reload watering program file.
        /// </summary>
        public WateringManager(IFeedbackService feedback, string settingsFolder, string controllerType)
        {
            if (!Directory.Exists(settingsFolder))
            {
                throw new InvalidDataException("working dir not exists");
            }
            s_FullSettingName = Path.Combine(settingsFolder, s_SettingsFileName);
            // create controller and scheduler
            Plan = ReadOrCreateWateringPlan();
            IWateringController controller = CreateController(controllerType);
            s_Scheduler = new WateringProgramScheduler(controller, feedback, Plan);

            // create file watchers
            s_PlanFileWatcher = new FileSystemWatcher(settingsFolder, s_SettingsFileName) { EnableRaisingEvents = false };
        }

        public void Start()
        {
            s_Scheduler.Start();
            if (!s_PlanFileWatcher.EnableRaisingEvents)
            {
                s_PlanFileWatcher.Changed += OnPlanFileModified;
                s_PlanFileWatcher.NotifyFilter = NotifyFilters.LastWrite;
                s_PlanFileWatcher.EnableRaisingEvents = true;
            }
        }

        public void Stop()
        {
            if (s_PlanFileWatcher.EnableRaisingEvents)
            {
                s_PlanFileWatcher.Changed -= OnPlanFileModified;
                s_PlanFileWatcher.EnableRaisingEvents = false;
            }
            s_Scheduler.Stop();
        }

        public void SavePlan()
        {
            using (XmlWriter writer = XmlWriter.Create(s_FullSettingName))
            {
                var serializer = new DataContractSerializer(typeof(WateringPlan));
                serializer.WriteObject(writer, Plan);
            }
        }

        private void OnPlanFileModified(object parent, FileSystemEventArgs args)
        {
            if (string.Equals(args.Name, s_SettingsFileName, StringComparison.CurrentCultureIgnoreCase))
            {
                // watering m_plan is changed, notify the scheduler.
                Plan = ReadOrCreateWateringPlan();
                s_Scheduler.UpdateWateringPlan(Plan);
            }
        }

        private WateringPlan ReadOrCreateWateringPlan()
        {
            if (!File.Exists(s_FullSettingName))
            {
                return new WateringPlan();
            }

            using (XmlTextReader reader = new XmlTextReader(s_FullSettingName))
            {
                var serializer = new DataContractSerializer(typeof(WateringPlan));
                return (WateringPlan)serializer.ReadObject(reader);
            }
        }

        private IWateringController CreateController(string controllerType)
        {
            IWateringController controller;
            switch (controllerType)
            {
                case "DummyController":
                    controller = new DummyController();
                    break;
                case "Adam6060Controller":
                    controller = new Adam6060Controller(Plan.RemoteControllerIpAddress);
                    break;
                default:
                    throw new InvalidDataException("controllerType " + controllerType + " is not supported");
            }
            return controller;
        }
    }
}
